1 /*
2 * Title: S/MIME Project
3 * Description: S/MIME email sending capabilities
4 * @Author Vladan Obradovic
5 * @Version 2.0.1
6 */
7
8 package org.webdocwf.util.smime.smime;
9
10
11 import org.webdocwf.util.smime.activation.CMSEnvelopedDataSource;
12 import org.webdocwf.util.smime.activation.CMSSignedDataSource;
13 import org.webdocwf.util.smime.exception.SMIMEException;
14 import org.webdocwf.util.smime.exception.ErrorStorage;
15 import org.webdocwf.util.smime.util.MimeAssist;
16 import org.webdocwf.util.smime.mail.MultipartGenerator;
17 import org.webdocwf.util.smime.util.ConvertAssist;
18 import org.webdocwf.util.smime.activation.StreamDataSource;
19 import javax.mail.internet.HeadersUtil;
20 import javax.mail.Session;
21 import javax.mail.Message;
22 import javax.mail.Multipart;
23 import javax.mail.Transport;
24 import javax.mail.MessagingException;
25 import javax.mail.internet.MimeMessage;
26 import javax.mail.internet.MimeBodyPart;
27 import javax.mail.internet.MimeMultipart;
28 import javax.mail.internet.InternetAddress;
29 import javax.activation.DataHandler;
30 import javax.activation.FileDataSource;
31 import java.util.Vector;
32 import java.util.Properties;
33 import java.util.SimpleTimeZone;
34 import java.util.GregorianCalendar;
35 import java.io.File;
36 import java.io.FileInputStream;
37 import java.io.InputStream;
38 import java.io.ByteArrayInputStream;
39 import java.security.Security;
40 import java.security.PrivateKey;
41 import java.security.KeyStore;
42 import java.security.cert.X509Certificate;
43 import java.security.cert.CertificateFactory;
44 import java.security.cert.CertificateException;
45 import sun.security.provider.Sun;
46 import org.bouncycastle.jce.provider.BouncyCastleProvider;
47
48
49 /***
50 * SignedAndEnvelopedSMIME class is used for creating and sending signed and
51 * encrypted, or encrypted and signed S/MIME messages. Which process will be
52 * first (encrypting or signing) is defined in the method signingAndEnveloping
53 * by using appropriate parameter: SIGN_FIRST or ENCRYPT_FIRST.<BR>
54 * <BR>
55 * Email message is in general composed of the content of the message and of one or
56 * more attachments. The content is visible part of the message, and attacments are
57 * mostly files or other binary data, which are not visible parts of message and
58 * which are used by email as a transport medium. In this implementation content
59 * can be represented in two different forms: <BR>
60 * <BR>
61 * <UL><LI>
62 * text/plain (only text withouth any formating) or
63 * </LI> <LI>
64 * text/html (html coded view of message)
65 * </LI></UL>
66 * Also, content can be absent, but than at least one attachment must be added.
67 * Content can be set on few manners. For text/plain type it can be done in time
68 * of construction with constructor designed special for creation of text/plain
69 * messages. Also, text content can be created by any of setContent() methods,
70 * if construction of object was done by other constructor which create object
71 * with empty content. Construction with other constructor offers a few different
72 * posibilities for importing content data (File, InputStream, String) by using
73 * appropriate setContent() method. If method with four parameters is used, 3rd
74 * ant 4th parameters are not in use for text/plain message and could be set
75 * null. For setting text/html content, construction of object should be done
76 * only by second mentioned constructor, which creates object with empty content.
77 * Content should be populated by html code with setContent() method. 3rd
78 * parameter is used for resolving relative addresses of resources in html
79 * code (images, movies...) and 4th parameter serves as data source for resources
80 * that are on special way addressed in html code. Also, there is a setContent()
81 * method which doesn't care about resources and which creates message content
82 * withouth them. For more information refer to setContent() methods.<BR>
83 * <BR>
84 * Message can contain any number of attachments. Also, message can
85 * be wihouth any attachment, but then content must be present. Every attachment
86 * should be added by performing single addAttachment() method. Attachments
87 * can be added from file or from InputStream. Mime-type which corresponds to
88 * particular attachment is obtained according to extension of file name
89 * (virtual or real file name) passed to addAttachment() method. File mime.types
90 * in META_INF directory contains list of mime-types and corresponding extensions
91 * which are used in determination of mime-type. File can be changed to satisfy
92 * secific requrements. For more information refer to addAttachmenttent()
93 * method.<BR>
94 * <BR>
95 * Encryption of message is performed by symmetric encryption with random
96 * generated symmetric key. This key is then encrypted by assymetric encryption
97 * with a recipient's public key, and sent together with encrypted message to
98 * recipient in CMS (Cryptographic Message Syntax) enveloped object. For all
99 * recipients of message (if there is more than one) operation of encrypting
100 * symmetric key must be performed with his corresponding public key (from .cer
101 * file). Encryption can be performed by following algorithms and corresponding
102 * key sizes:<BR>
103 * RC2_CBC, 40 bits (default encryption)<BR>
104 * RC2_CBC, 64 bits<BR>
105 * RC2_CBC, 128 bits<BR>
106 * DES, 56 bits<BR>
107 * DES_EDE3_CBC, 128 bits<BR>
108 * DES_EDE3_CBC, 192 bits<BR>
109 * <BR>
110 * As a asymmetric algorithm, RSA algorithm is used.<BR>
111 * <BR>
112 * Message is implicitly signed in the case of both processing (signing
113 * and enveloping). External signing allows email receiving clients withouth
114 * implemented SMIME capabilities to preview the signed SMIME email messages.
115 * This possibilities have no importance in the case of both signing and enveloping
116 * (or enveloping and signing) because, before or after signing, the message is
117 * encrypted, so it is not readable.<BR>
118 * <BR>
119 * Message can be signed with or without Signed Attributes. Signed Attributes
120 * are one optional part of CMS (Cryptographic Message Syntax) signed objects,
121 * and consist of some atributes used in the process of signing (date and time
122 * of signing, capabilities of sending email client, message digest value...).
123 * If those attributes are ommited, only pure message is taken in the process
124 * of signing.<BR>
125 * <BR>
126 * Digest algorithm can be SHA1, MD2 or MD5 which depends on selected signing algorithm.<BR>
127 * <BR>
128 * Capabilities Attributes are one of Signed Attributes, and in the process of
129 * signing (if Signed Attributes are involved) can be set. This attributes
130 * indicate to recipient email client which encipher, symmetric and/or signature
131 * algorithms signer's email client preferes, end they can be used in the next
132 * communication between each others. Setting this posibilities is optional, but
133 * if it is set, order of adding gives the information about most preferes algorithms
134 * within paricular group of algorithms. Defined Capabilities Attributes in this version
135 * of Signed SMIME can be from group: RC2 40, RC2 64, RC2 128, DES and DES_EDE3 for
136 * symmetric encryption algorihms, from group: MD2 with RSA, MD5 with RSA, SHA1 with RSA
137 * and SHA1 with DSA for signing algorithms, and RSA for encipher algorithm. For more
138 * information see setCapabilities method in this class.<BR>
139 * <BR>
140 * Certificates of signers and their root authorities can be included in the
141 * signed message. This posibilities allow the recipient of signed SMIME
142 * message to automatically include signer's certificates as trusted, and verify
143 * signed message. This posibilities are optional.<BR>
144 * <BR>
145 * More than one signer can perform signing of message and they can use
146 * different signing algorithms. Digital signing can be performed by SHA1_WITH_RSA,
147 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA.<BR>
148 * <BR>
149 */
150 public class SignedAndEnvelopedSMIME {
151
152 /***
153 * Container for MIME message
154 */
155 private MimeMessage message;
156
157 /***
158 * Storage for .cer files corresponding to appropriate enveloping session
159 */
160 private Vector certArray = new Vector(0, 1);
161
162 /***
163 * Storage for .pfx files corresponding to appropriate signing session (used
164 * for first type of addSigner function).
165 */
166 private Vector ksArray = new Vector(0, 1);
167
168 /***
169 * Storage for digest algorithm corresponding to appropriate signing session
170 * (used for first type of addSigner function).
171 */
172 private Vector digestArray = new Vector(0, 1);
173
174 /***
175 * Storage for byte[2] grouped indicators (used for first type of addSigner
176 * function).
177 */
178 private Vector including = new Vector(0, 1);
179
180 /***
181 * Storage for certificate chain corresponding to appropriate signing session
182 * (used for second type of addSigner function)
183 */
184 private Vector certChainArray = new Vector(0, 1);
185
186 /***
187 * Storage for private key corresponding to appropriate signing session (used
188 * for second type of addSigner function)
189 */
190 private Vector privKeyArray = new Vector(0, 1);
191
192 /***
193 * Storage for digest algorithm corresponding to appropriate signing session
194 * (used for second type of addSigner function)
195 */
196 private Vector digestArray2 = new Vector(0, 1);
197
198 /***
199 * Storage for byte[2] grouped indicators (used for second type of addSigner
200 * function)
201 */
202 private Vector including2 = new Vector(0, 1);
203
204 /***
205 * Storage for MIME bodyparts
206 */
207 private Vector bodyPartArray = new Vector(0, 1);
208
209 /***
210 * Storage for additional certificates
211 */
212 private Vector aditionalCerts = new Vector(0, 1);
213
214 /***
215 * Temporary storage for capabilities (after method addSigner, this object is
216 * copied to capabilities or capabilities2).
217 */
218 private Vector capabilitiesTemp = new Vector(0, 1);
219
220 /***
221 * Storage for capabilities (used for first type of addSigner function)
222 */
223 private Vector capabilities = new Vector(0, 1);
224
225 /***
226 * Storage for capabilities (used for second type of addSigner function)
227 */
228 private Vector capabilities2 = new Vector(0, 1);
229
230 /***
231 * Indication that at least one recipient must be TO (others may be CC or BCC)
232 */
233 private boolean indicatorTo = false;
234
235 /***
236 * Indicator of presence of plain text content
237 */
238 private boolean textContentPresence = false;
239
240 /***
241 * Initializes the JavaMail session for SMTP and MimeMessage for signing.
242 * Dynamically loads the BC and SUN provider necessary for encryption. This
243 * constructor is used for creating message with text/plain content. For creating
244 * html formated content (text/html), other constructor should be used in
245 * combination with one of setContent methods. Note that after using this
246 * constructor setContent method can be used only if "content" argument of
247 * constructor was given as null, otherwise setContent method can't be used
248 * because content is already set as text/plain.
249 * @param smtpHost name of SMTP host used for sending email
250 * @param fromAddress email address of sender (FROM field in email header)
251 * @param subject subject of email (SUBJECT field in email header)
252 * @param content text/plain content of email message
253 * @exception SMIMEException if smtpHost or fromAddress parameters are null.
254 * Also, it can be caused by non SMIMEException which is MessagingException.
255 */
256 public SignedAndEnvelopedSMIME(String smtpHost, String fromAddress, String subject,
257 String content) throws SMIMEException {
258 try {
259 Security.addProvider(new BouncyCastleProvider()); // Dynamic loading the BC provider necessary for encryption
260 Security.addProvider(new Sun()); // Dynamic loading the SUN provider
261
262 if (smtpHost == null | fromAddress == null)
263 throw new SMIMEException(this, 1041);
264 Properties sesProp = new Properties();
265
266 sesProp.setProperty("mail.smtp.host", smtpHost);
267 Session ses = Session.getInstance(sesProp);
268
269 message = new MimeMessage(ses);
270 InternetAddress from = new InternetAddress(fromAddress);
271
272 message.setFrom(from);
273 if (subject != null)
274 message.setSubject(subject);
275 if (content != null) {
276 MimeBodyPart mbp = new MimeBodyPart();
277
278 mbp.setText(content);
279 bodyPartArray.addElement(mbp);
280 textContentPresence = true;
281 }
282 } catch (Exception e) {
283 throw SMIMEException.getInstance(this, e, "constructor");
284 }
285 }
286
287 /***
288 * Initializes the JavaMail session for SMTP and MimeMessage for signing.
289 * Dynamically loads the BC and SUN provider necessary for encryption. This
290 * constructor does not create content of message and it can be set later with
291 * one of setContent methods. Also, message can be left withouth content, but
292 * then at least one attachement must be added.
293 * @param smtpHost name of SMTP host used for sending email
294 * @param fromAddress email address of sender (FROM field in email header)
295 * @param subject subject of email (SUBJECT field in email header)
296 * @exception SMIMEException if smtpHost or fromAddress parameters are null.
297 * Also, it can be caused by non SMIMEException which is MessagingException.
298 */
299 public SignedAndEnvelopedSMIME(String smtpHost, String fromAddress, String subject)
300 throws SMIMEException {
301 this(smtpHost, fromAddress, subject, null);
302 }
303
304 /***
305 * Sets message content. Message content can be given in two differrent forms:
306 * text and html code. If content is type of text, parameter "type" should be
307 * "text/plain" and other two parameters are not in use (should be set null).
308 * If content is type of html code, parameter "type" should be set as "text/html",
309 * otherwise (if it is set as "text/plain") html code is processed as a plain
310 * text. This method can be performed only once.<BR>
311 * <BR>
312 * In case of html content, it is essential to (on appropriate way) associate some
313 * attributes of particular elements in html code ("background" or "src" atributes)
314 * with corresponding ressources (URL-s, relative file addresses or byte array
315 * streams). This resources should all be sent with message to enable recipient
316 * to see complete html message. Location of resources can be given in few
317 * different forms and depending on that, allocation resolving can be successful or
318 * not. Following text represents different possibilities for defining locations
319 * of resources (pictures, animations, sound...) inside of html code passed to
320 * this method, and necessery passed additional parameters used for resolving
321 * this resource locations.<BR>
322 * <BR>
323 * <UL>
324 * <LI>URL defined as: http://... is left unchanged. This resource is not sent
325 * with the message and it couldn't be seen by recipient if it is not online on
326 * the internet.</LI>
327 * <LI>URL defined as: file://... is transformed to corresponding Content ID if
328 * the resource can be found on specified location and it is sent with message.</LI>
329 * <LI>Absolute path, for example defined as: "c:\tmp\test\picture.bmp", is
330 * transformed to corresponding Content ID if the resource can be found on
331 * specified location, and is sent with message. If all resources in html
332 * code are specified with its absolute path, the 3rd parameter in this method
333 * can be null.</LI>
334 * <LI>Relative path of all resources specified in html code, for example
335 * defined as: ".\test\picture.bmp" and ".\example\flush.swf", must be defined
336 * to be relative to same directory path (in this case it is c:\tmp). This parameter
337 * (common directory path) is given as 3rd parameter in this method, and is named
338 * "path". If html code is obtained from .html file, necessery common directory
339 * path is usually path to this .html file. Location of resource is transformed
340 * to corresponding Content ID if the resource can be found on specified location.
341 * This location is sent with the message.</LI>
342 * <LI>Byte array stream as resource for html attribute must be referenced from
343 * html code as: <BR>
344 * <BR><PRE>
345 * *****nnn<virtual_file_name><BR>
346 * <BR></PRE>
347 * Five '*' characters (must be five) define that it is resource expected from
348 * the stream. Other three characters must be digits (000-999) and represent
349 * index of corresponding stream in stream array. virtual_file_name is name and
350 * extension assigned to data passed from stream. Name is used in construction of
351 * "name" parameter in Content-Type, while extension of file name is used in
352 * detection of appropriate mime-type. Lenght of virtual_file_name is not
353 * important. If there is no data referenced from byte array stream ,4th
354 * parameter of this method named "resources" can be null. Also, if all resources
355 * are passed through the array of streams, 3th parameter ("path") can be null.
356 * Location of resource is transformed to corresponding Content ID if no error
357 * has occured during the process of allocation.</LI>
358 * </UL>
359 * <BR>
360 * All mentioned resource allocation types can be combined together in the same
361 * html code, and all will be processed with appropriate use of this method.<BR>
362 * <BR>
363 * Note that number of resource references that are defined in html code by
364 * using virtual_file_names must be greater than or equal to number of elements
365 * in array of InputStream (4th parameter). If one resource (one element in array
366 * of IputStream) is used in html code more than once, it is advisable to use
367 * same virtual_file_name in html code because message is then sent with only
368 * one attached resource (image, movie...). It is essetntial that desired resource
369 * in input stream array corresponds to specified "nnn" part of virtual_file_name.<BR>
370 * <BR>
371 * If resources specified on any described name can not be found or resolved,
372 * or if any exception has occured during its processing, they won't be added and
373 * html message will be sent withouth them.
374 * @param content String representation of message content (text or html code).
375 * @param type type of given content. It can take values: "text/plain" or
376 * "text/html".
377 * @param path common directory path for relative file locations in html code.
378 * It can be null if all resources set absolute path or are defined by
379 * byte array streams, or if sending resources with relative address it is not desired.
380 * Also, it is set to null in case of text/plain message.
381 * @param resources way for representing resources used in the given html code
382 * which will be added to message as array of InputStream. Detail use
383 * of this argument is described above. It can be null if no resources as byte
384 * array stream are used, or if sending resources given in that way is not desired.
385 * Also, it is set to null in case of text/plain message.
386 * @exception SMIMEException if content is tried to be added twice, or in case of
387 * wrong "type" parameter. Also, it can be caused by non SMIMEException which can
388 * be one of the following: MessagingException UnsoportedEncodingException.
389 */
390 public void setContent(String content, String type, String path,
391 InputStream[] resources) throws SMIMEException {
392
393 if (content != null) {
394 ByteArrayInputStream bais = null;
395
396 try {
397 bais = new ByteArrayInputStream(content.getBytes("ISO-8859-1"));
398 } catch (Exception e) {
399 throw SMIMEException.getInstance(this, e, "setContent");
400 }
401 this.setContent(bais, type, path, resources);
402
403 } else
404 throw new SMIMEException(this, 1035);
405 }
406
407 /***
408 * Sets message content from InputStream. This method can be performed only once.
409 * Message content can be given in two differrent forms: text and html code. If
410 * content is type of text, parameter "type" should be "text/plain", while if
411 * content is type of html code, parameter "type" should be set as "html/code".
412 * For further information refer to setContent method with four arguments
413 * (String, String, String, InputStream[] ) which is called by this method.
414 * @param content message content data given from any InputStream.
415 * Data can be text or html code and will be interpreted according to second
416 * parameter: "type".
417 * @param type type of given content. It can take values: "text/plain" or
418 * "text/html".
419 * @param path common directory path for relative file locations in html code.
420 * It can be null if all resources in html code have set absolute path or are
421 * defined by byte array streams, or if sending resources with relative address
422 * is not desired. Also, it is set to null in case of text/plain message.
423 * @param resources way for representing resources used in the given html code
424 * which will be added to message as array of InputStreams. Detail use
425 * of this argument is described in other setContent methods mentioned before.
426 * It can be null if no resources as byte array stream are used, or if sending
427 * resources given in that way is not desired. Also, it is set to null in case
428 * of text/plain message.
429 * @exception SMIMEException if content is tried to be added twice , in case of
430 * wrong "type" parameter or in case when parameter content is null. Also, it can
431 * be caused by non SMIMEException which is MessagingException.
432 */
433 public void setContent(InputStream content, String type, String path,
434 InputStream[] resources) throws SMIMEException {
435 if (textContentPresence)
436 throw new SMIMEException(this, 1049);
437 if (content != null) {
438 try {
439 if (type.equalsIgnoreCase("text/plain")) {
440 MimeBodyPart mbp = new MimeBodyPart();
441 String temp = new String(ConvertAssist.inStreamToByteArray(content), "ISO-8859-1");
442
443 mbp.setText(temp, "ISO-8859-1");
444 bodyPartArray.add(0, mbp);
445 textContentPresence = true;
446 } else if (type.equalsIgnoreCase("text/html")) {
447 MimeMultipart htmlMultipart =
448 MultipartGenerator.getHtmlMultipart(content, path, resources);
449
450 bodyPartArray.add(0, htmlMultipart);
451 textContentPresence = true;
452 } else
453 throw new SMIMEException(this, 1048);
454 } catch (Exception e) {
455 throw SMIMEException.getInstance(this, e, "setContent");
456 }
457 } else
458 throw new SMIMEException(this, 1035);
459 }
460
461 /***
462 * Sets message content from InputStream. This method can be performed only once.
463 * Message content can be given in two differrent forms: text and html code. If
464 * content is type of text, parameter "type" should be "text/plain", while if
465 * content is type of html code, parameter "type" should be set as "html/code".
466 * If html code content is set by this method, message will be generated withouth
467 * inclusion of the resources defined in paricular html element's attribute ("src" and
468 * "background"). Only plain html code will be sent and any reference to local
469 * file system resources will be useless for recipient of the message. HTTP referenced
470 * resources can still be available if recipient is online on Internet. Message
471 * generated on this way is smaller so encrypting process should be faster.
472 * @param content message content data given from any InputStream.
473 * Data could be text or html code and will be interpreted according to second
474 * parameter: "type".
475 * @param type type of given content. It can take values: "text/plain" or
476 * "text/html".
477 * @exception SMIMEException if content is tried to be added twice , in case of
478 * wrong "type" parameter or in case when parameter content is null. Also, it can
479 * be caused by non SMIMEException which is MessagingException.
480 */
481 public void setContent(InputStream content, String type) throws SMIMEException {
482 if (textContentPresence)
483 throw new SMIMEException(this, 1049);
484 if (content != null) {
485 try {
486 if (type.equalsIgnoreCase("text/plain")) {
487 MimeBodyPart mbp = new MimeBodyPart();
488 String temp = new String(ConvertAssist.inStreamToByteArray(content), "ISO-8859-1");
489
490 mbp.setText(temp, "ISO-8859-1");
491 bodyPartArray.add(0, mbp);
492 textContentPresence = true;
493 } else if (type.equalsIgnoreCase("text/html")) {
494 MimeMultipart htmlMultipart =
495 MultipartGenerator.getHtmlMultipart(content);
496
497 bodyPartArray.add(0, htmlMultipart);
498 textContentPresence = true;
499 } else
500 throw new SMIMEException(this, 1048);
501 } catch (Exception e) {
502 throw SMIMEException.getInstance(this, e, "setContent");
503 }
504 } else
505 throw new SMIMEException(this, 1035);
506 }
507
508 /***
509 * Sets message content from String. This method can be performed only once.
510 * Message content can be given in two differrent forms: text and html code. If
511 * content is type of text, parameter "type" should be "text/plain", while if
512 * content is type of html code, parameter "type" should be set as "html/code".
513 * If html code content is set by this method, message will be generated withouth
514 * inclusion of the resources defined in paricular html element's attribute ("src" or
515 * "background"). Only plain html code will be sent and any reference to local
516 * file system resources will be useless for recipient of the message. HTTP referenced
517 * resources can still be available if recipient is online on Internet. Message
518 * generated on this way is smaller, so encrypting process should be faster.
519 * @param content message content data given as String which can
520 * be text or html code and will be interpreted according to second parameter:
521 * "type".
522 * @param type type of given content. It can take values: "text/plain" or
523 * "text/html".
524 * @exception SMIMEException if content is tried to be added twice, or in case of
525 * wrong "type" parameter. Also, it can be caused by non SMIMEException which can
526 * be one of the following: MessagingException UnsoportedEncodingException.
527 */
528 public void setContent(String content, String type) throws SMIMEException {
529
530 if (content != null) {
531 ByteArrayInputStream bais = null;
532
533 try {
534 bais = new ByteArrayInputStream(content.getBytes("ISO-8859-1"));
535 } catch (Exception e) {
536 throw SMIMEException.getInstance(this, e, "setContent");
537 }
538 this.setContent(bais, type);
539
540 } else
541 throw new SMIMEException(this, 1035);
542 }
543
544 /***
545 * Sets message content from file represented by File object. This method can be
546 * performed only once. Message content can be given in two differrent forms:
547 * text and html code. If content is type of text, parameter "type" should be
548 * "text/plain", while if content is type of html code, parameter "type" should
549 * be set as "html/code". For further information refer to setContent method
550 * with four arguments (String, String, String, InputStream[] ) which is called
551 * by this method.
552 * @param inFile location of file which is used for content of the message
553 * @param type type of given content. It can take values: "text/plain" or
554 * "text/html".
555 * @exception SMIMEException if content is tried to be added twice, in case of
556 * wrong "type" parameter, or if passed file (as File object) does not exist in
557 * file sistem. Also, it can be caused by non SMIMEException which can be one of
558 * the following: MessagingException or IOException.
559 */
560 public void setContent(File inFile, String type) throws SMIMEException {
561
562 if (textContentPresence)
563 throw new SMIMEException(this, 1049);
564 if (inFile != null && inFile.exists()) {
565 try {
566 File inFileAbs = inFile.getAbsoluteFile().getCanonicalFile();
567 String content = ConvertAssist.readFileToString(inFileAbs);
568
569 this.setContent(content, type, inFile.getParent(), null);
570 } catch (Exception e) {
571 throw SMIMEException.getInstance(this, e, "setContent");
572 }
573 } else
574 throw new SMIMEException(this, 1034);
575 }
576
577 /***
578 * Sets REPLY TO field in message header
579 * @param replyAddress email address used to reply
580 * @exception SMIMEException caused by non SMIMEException which is
581 * MessagingException. Also, javax.mail.internet.AddressException is thrown
582 * from instances of InternetAddress class (but AddressException extends
583 * MessagingException).
584 */
585 public void setReply(String replyAddress) throws SMIMEException {
586 try {
587 InternetAddress reply[] = new InternetAddress[1];
588
589 reply[0] = new InternetAddress(replyAddress);
590 message.setReplyTo(reply);
591 } catch (Exception e) {
592 throw SMIMEException.getInstance(this, e, "addRecipient");
593 }
594 }
595
596 /***
597 * Adds recipient address, type and .cer file of email recipient to signed and
598 * enveloped S/MIME message.
599 * @param recipientAddress email address of recipent (fields TO or CC or BCC
600 * in email message header)
601 * @param type should be TO, CC or BCC
602 * @param cerFileName path and file name with certificate corresponding
603 * to recipient (file with .cer extension)
604 * @exception SMIMEException if type of addressing of messages is not TO, CC
605 * or BCC.
606 * @exception SMIMEException caused by non SMIMEException which is
607 * MessagingException.
608 */
609 public void addRecipient(String recipientAddress, String type, String cerFileName)
610 throws SMIMEException {
611 try {
612 if (!type.equalsIgnoreCase("TO") & !type.equalsIgnoreCase("BCC") & !type.equalsIgnoreCase("CC"))
613 throw new SMIMEException(this, 1042);
614 if (type.equalsIgnoreCase("TO")) {
615 message.addRecipients(Message.RecipientType.TO, recipientAddress);
616 indicatorTo = true;
617 } else if (type.equalsIgnoreCase("CC"))
618 message.addRecipients(Message.RecipientType.CC, recipientAddress);
619 else if (type.equalsIgnoreCase("BCC"))
620 message.addRecipients(Message.RecipientType.BCC, recipientAddress);
621 } catch (Exception e) {
622 throw SMIMEException.getInstance(this, e, "addRecipient");
623 }
624 certArray.addElement(cerFileName); // adding location of .cer file in stack
625 }
626
627 /***
628 * Adds file as attachment to email message
629 * @param fileName path and file name used for attachment
630 * @exception SMIMEException if passed file (as File object) does not exist in
631 * file sistem. Also, it can be caused by non SMIMEException which is
632 * MessagingException
633 */
634 public void addAttachment(String fileName) throws SMIMEException {
635 File fn = new File(fileName);
636
637 this.addAttachment(fn);
638 }
639
640 /***
641 * Adds file as attachment to email message
642 * @param file used for attachment represented as File object
643 * @exception SMIMEException if passed file (as File object) does not exist in
644 * file sistem. Also, it can be caused by non SMIMEException which is
645 * MessagingException
646 */
647 public void addAttachment(File file) throws SMIMEException {
648 if (!file.exists())
649 throw new SMIMEException(this, 1034);
650 MimeBodyPart attachment = new MimeBodyPart();
651 FileDataSource fd = new FileDataSource(file);
652
653 try {
654 attachment.setDataHandler(new DataHandler(fd));
655 attachment.setDisposition(attachment.ATTACHMENT);
656 attachment.setFileName(file.getName());
657 } catch (Exception e) {
658 throw SMIMEException.getInstance(this, e, "addAttachment");
659 }
660
661 bodyPartArray.addElement(attachment);
662 }
663
664 /***
665 * Adds data from InputStream as attachment to email message
666 * @param data byte array from InputStream
667 * @param fileName virtual or real file name (wihouth path). Correct information
668 * about name; extension of file name is especially important. Name
669 * is used in construction of "name" parameter in Content-Type header line of
670 * body parts of mime message. Extension of file is used in detection of
671 * appropriate mime-type.
672 * @exception SMIMEException caused by non SMIMEException which is
673 * MessagingException
674 */
675 public void addAttachment(InputStream data, String fileName) throws SMIMEException {
676 MimeBodyPart attachment = new MimeBodyPart();
677
678 try {
679 attachment.setDataHandler(new DataHandler(new StreamDataSource(data, fileName)));
680 attachment.setDisposition(attachment.ATTACHMENT);
681 attachment.setFileName(fileName);
682 bodyPartArray.addElement(attachment);
683 } catch (Exception e) {
684 throw SMIMEException.getInstance(this, e, "addAttachment");
685 }
686 }
687
688 /***
689 * Sets Capabilities Attributes (method is optional, but if exists, must be
690 * performed before addSigner method). Depending on parameter type0, other five
691 * parameters make order in specific group of algorithms. Groups of algorithms
692 * with positions of specific algorithms are:<BR>
693 * (SIGNATURE, MD2 with RSA, MD5 with RSA, SHA1 with RSA, SHA1 with DSA, Unused field)<BR>
694 * (SYMMETRIC, RC2 40 bits, RC2 64 bits, RC2 128 bits, DES, DES_EDE3)<BR>
695 * (ENCIPHER, RSA, Unused field, Unused field, Unused field, Unused field)<BR>
696 * <BR>
697 * For example, if we wish to set Capabilities Attributes for symmetric algorithms
698 * in order: RC2 64 bits, RC2 40 bits and DES, encipher algorithm RSA (only possible
699 * in this version), and signature algorithms in order: SHA1 with RSA, MD5 with RSA
700 * and MD2 with RSA, we should make following lines of code<BR>
701 * <BR>
702 * setCapabilities ("SYMMETRIC", 2, 1, 0, 3, 0)<BR>
703 * setCapabilities ("ENCIPHER", 1, 0, 0, 0, 0)<BR>
704 * setCapabilities ("SIGNATURE", 3, 2, 1, 0, 0)<BR>
705 * <BR>
706 * 0 means exclusion of algorithm from the specified position in the method. It is
707 * free to decide which algorithm will be included, or which group of algorithm
708 * will be included in Capabilities Attributes. If no groups are added, capabilities
709 * attributes won't be added to Signed Attributes. If two or more signers will
710 * sign the message, and their capabilities are different, this method should
711 * be performed before every signing if we wish to specify Capabilities
712 * Attributes for all particular signers. If type0 parameter is set as:<BR>
713 * setCapabilities ("DEFAULT", 0, 0, 0, 0, 0)<BR>
714 * it is equivalent to:<BR>
715 * setCapabilities ("SYMMETRIC", 1, 0, 0, 0, 0)<BR>
716 * setCapabilities ("ENCIPHER", 0, 0, 1, 0, 0)<BR>
717 * setCapabilities ("SIGNATURE", 1, 0, 0, 0, 0)<BR>
718 * @param type0 sets group of algorithms for capabilities attributes. It can be set
719 * with values: SIGNATURE, SYMMETRIC, ENCIPHER or DEFAULT.
720 * @param par10 sets order in group of parameters, or exclude some algorithms
721 * from capabilities atributes. Can take values 1, 2, 3, 4 or 5 and 0 for
722 * exclusion of the particular algorithm.
723 * @param par20 same as for par10
724 * @param par30 same as for par10
725 * @param par40 same as for par10
726 * @param par50 same as for par10
727 * @exception SMIMEException if method is performed more than three times for one signer,
728 * or in case of wrong values of parameters.
729 */
730 public void setCapabilities(String type0, int par10, int par20, int par30,
731 int par40, int par50) throws SMIMEException {
732 int[] tempType = { par10, par20, par30, par40, par50 };
733
734 capabilitiesTemp.addElement(type0);
735 capabilitiesTemp.addElement(tempType);
736 if (capabilitiesTemp.size() > 6)
737 throw new SMIMEException(this, 1045);
738 }
739
740 /***
741 * Adds signer to signed and enveloped S/MIME message.
742 * @param pfxfileName path and file name with certificate and private key
743 * corresponding to the sender of the message (file with .p12 or .pfx extension)
744 * @param password used to access to .pfx or .p12 file
745 * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
746 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
747 * @param includingCert including/not including certificates to signed
748 * message
749 * @param includingSignAttrib including/not including signed attributes
750 * to signed message. Must be set to true in case of implicit signing
751 * @exception SMIMEException caused by non SMIMEException which can be one of the
752 * following: FileNotFoundException, NoSuchProviderException, KeyStoreException
753 * CertificateException, NoSuchAlgorithmException or IOException.
754 */
755 public void addSigner(String pfxfileName, String password, String signingAlg,
756 boolean includingCert, boolean includingSignAttrib) throws SMIMEException {
757 try {
758 char[] paswCh = password.toCharArray();
759 FileInputStream inPFX = new FileInputStream(pfxfileName);
760 KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
761
762 ks.load(inPFX, paswCh);
763 inPFX.close();
764 boolean[] incl = { includingCert, includingSignAttrib };
765
766 ksArray.addElement(ks);
767 digestArray.addElement(signingAlg);
768 including.addElement(incl);
769 if (capabilitiesTemp.size() != 0) {
770 for (int i = 0; i != capabilitiesTemp.size(); i++)
771 capabilities.addElement(capabilitiesTemp.elementAt(i));
772 }
773 for (int i = 0; i != (6 - capabilitiesTemp.size()); i++)
774 capabilities.addElement(null);
775 capabilitiesTemp = new Vector(0, 1);
776 } catch (Exception e) {
777 throw SMIMEException.getInstance(this, e, "addSigner");
778 }
779 }
780
781 /***
782 * Adds signer to signed and enveloped S/MIME message.
783 * @param chain certificate chain. First certificate in array must be
784 * owner's certificate, and last certificate has to be root certificate
785 * @param privKey private key corresponding to owner's certificate (DSA
786 * or RSA depend on type of signing)
787 * @param signingAlg algorithm used for signing (can be SHA1_WITH_RSA,
788 * MD2_WITH_RSA, MD5_WITH_RSA or SHA1_WITH_DSA).
789 * @param includingCert including/not including certificates to signed
790 * message
791 * @param includingSignAttrib including/not including signed attributes
792 * to signed message. Must be set to true in case of implicit signing
793 */
794 public void addSigner(X509Certificate[] chain, PrivateKey privKey,
795 String signingAlg, boolean includingCert, boolean includingSignAttrib) {
796 boolean[] incl = { includingCert, includingSignAttrib };
797
798 certChainArray.addElement(chain);
799 privKeyArray.addElement(privKey);
800 digestArray2.addElement(signingAlg);
801 including2.addElement(incl);
802 if (capabilitiesTemp.size() != 0) {
803 for (int i = 0; i != capabilitiesTemp.size(); i++)
804 capabilities2.addElement(capabilitiesTemp.elementAt(i));
805 }
806 for (int i = 0; i != (6 - capabilitiesTemp.size()); i++)
807 capabilities2.addElement(null);
808 capabilitiesTemp = new Vector(0, 1);
809 }
810
811 /***
812 * Adds additional certificate to signed message
813 * @param cert X509 certificate
814 */
815 public void addCertificate(X509Certificate cert) {
816 aditionalCerts.addElement(cert);
817 }
818
819 /***
820 * Signes and envelopes message with default algorithm RC2, 40 bits
821 * @param type defines which action will be performed first (signing or
822 * enveloping). Allowed parameters are: SIGN_FIRST (signing first and then
823 * enveloping), and ENCRYPT_FIRST (enveloping first and then signing).
824 * @exception SMIMEException if one of recipients is not declared as TO
825 * recipient, if there is no message for enveloping, or if parameter "type"
826 * for message protection order in not SIGN_FIRST or ENCRYPT_FIRST. Also, it
827 * can be caused by non SMIMEException which can be one of the following:
828 * CertificateException, IOException, MessagingException, or FileNotFoundException.
829 */
830 public void signingAndEnveloping(String type) throws SMIMEException {
831 this.signingAndEnveloping("RC2_CBC", 40, type); // type can take values "SIGN_FIRST" or "ENCRYPT_FIRST"
832 }
833
834 /***
835 * Signes and envelopes message with given algorithm name and key length
836 * @param algorithmName name of chosen algorithm used for encryption
837 * @param keyLength key size in bits
838 * @param type defines which action will be performed first (signing or
839 * enveloping). Allowed parameters are: SIGN_FIRST (signing first and than
840 * enveloping), and ENCRYPT_FIRST (enveloping first and than signing).
841 * @exception SMIMEException if one of recipients is not declared as TO
842 * recipient, if there is no message for enveloping or if parameter "type"
843 * for message protection order in not SIGN_FIRST or ENCRYPT_FIRST. Also, it
844 * can be caused by non SMIMEException which can be one of the following:
845 * CertificateException, IOException, MessagingException, or FileNotFoundException.
846 */
847 public void signingAndEnveloping(String algorithmName, int keyLength, String type) throws SMIMEException {
848 try {
849 if ((!type.equalsIgnoreCase("SIGN_FIRST")) && (!type.equalsIgnoreCase("ENCRYPT_FIRST")))
850 throw new SMIMEException(this, 1046);
851
852 if (indicatorTo != true)
853 throw new SMIMEException(this, 1043);
854 if (textContentPresence & bodyPartArray.size() == 1) { // message contains only content
855 if (bodyPartArray.elementAt(0) instanceof MimeBodyPart) { // text/plain message
856 MimeBodyPart contentBody = (MimeBodyPart) bodyPartArray.elementAt(0);
857
858 message.setContent((String) contentBody.getContent(), contentBody.getContentType());
859 message.setDisposition(message.INLINE);
860 } else // text/html message
861 message.setContent((MimeMultipart) bodyPartArray.elementAt(0));
862 } else if (bodyPartArray.size() != 0) {
863 Multipart mp = new MimeMultipart();
864
865 for (int i = 0; i != bodyPartArray.size(); i++) {
866 if (bodyPartArray.elementAt(i) instanceof MimeMultipart) {
867 MimeBodyPart forMulti = new MimeBodyPart();
868
869 forMulti.setContent((MimeMultipart) bodyPartArray.elementAt(i));
870 mp.addBodyPart(forMulti);
871 } else
872 mp.addBodyPart((MimeBodyPart) bodyPartArray.elementAt(i));
873 }
874 message.setContent(mp);
875 } else
876 throw new SMIMEException(this, 1044);
877
878 CMSSignedDataSource sigDataSource = null;
879 CMSEnvelopedDataSource envDataSource = null;
880
881 if (type.equalsIgnoreCase("SIGN_FIRST")) {
882 sigDataSource = new CMSSignedDataSource(message, false);
883 for (int i = 0; i < ksArray.size(); i++) {
884 boolean[] incl = (boolean[]) including.elementAt(i);
885
886 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities.elementAt(j) != null; j = j + 2) {
887 int[] capabil = (int[]) capabilities.elementAt(j + 1);
888
889 sigDataSource.setCapabilities((String) capabilities.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]);
890 }
891 sigDataSource.addSigner((KeyStore) ksArray.elementAt(i), incl[0], incl[1], (String) digestArray.elementAt(i));
892 }
893 for (int i = 0; i < certChainArray.size(); i++) {
894 boolean[] incl2 = (boolean[]) including2.elementAt(i);
895
896 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities2.elementAt(j) != null; j = j + 2) {
897 int[] capabil = (int[]) capabilities2.elementAt(j + 1);
898
899 sigDataSource.setCapabilities((String) capabilities2.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]);
900 }
901 sigDataSource.addSigner((X509Certificate[]) certChainArray.elementAt(i), (PrivateKey) privKeyArray.elementAt(i), incl2[0], incl2[1], (String) digestArray2.elementAt(i));
902 }
903 for (int i = 0; i < aditionalCerts.size(); i++) {
904 sigDataSource.addCertificate((X509Certificate) aditionalCerts.elementAt(i));
905 }
906
907 message.setDataHandler(new DataHandler(sigDataSource));
908 HeadersUtil.updateHeaders(message);
909 envDataSource = new CMSEnvelopedDataSource(MimeAssist.messageConvertor(message),
910 algorithmName, keyLength);
911
912 for (int i = 0; i != certArray.size(); i++) {
913 InputStream inStream = new FileInputStream((String) (certArray.elementAt(i)));
914 CertificateFactory cf = CertificateFactory.getInstance("X.509");
915 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
916
917 inStream.close();
918 envDataSource.addRecipient(cert);
919 }
920 message.setDataHandler(new DataHandler(envDataSource));
921 message.setDescription("Signed and Enveloped SMIME message.");
922 } else {
923 envDataSource = new CMSEnvelopedDataSource(message, algorithmName, keyLength);
924 for (int i = 0; i != certArray.size(); i++) {
925 InputStream inStream = new FileInputStream((String) (certArray.elementAt(i)));
926 CertificateFactory cf = CertificateFactory.getInstance("X.509");
927 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
928
929 inStream.close();
930 envDataSource.addRecipient(cert);
931 }
932
933 message.setDataHandler(new DataHandler(envDataSource));
934 HeadersUtil.updateHeaders(message);
935 sigDataSource = new CMSSignedDataSource(MimeAssist.messageConvertor(message),
936 false);
937
938 for (int i = 0; i < ksArray.size(); i++) {
939 boolean[] incl = (boolean[]) including.elementAt(i);
940
941 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities.elementAt(j) != null; j = j + 2) {
942 int[] capabil = (int[]) capabilities.elementAt(j + 1);
943
944 sigDataSource.setCapabilities((String) capabilities.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]);
945 }
946 sigDataSource.addSigner((KeyStore) ksArray.elementAt(i), incl[0], incl[1], (String) digestArray.elementAt(i));
947 }
948 for (int i = 0; i < certChainArray.size(); i++) {
949 boolean[] incl2 = (boolean[]) including2.elementAt(i);
950
951 for (int j = 6 * i; j != (6 * (i + 1)) && capabilities2.elementAt(j) != null; j = j + 2) {
952 int[] capabil = (int[]) capabilities2.elementAt(j + 1);
953
954 sigDataSource.setCapabilities((String) capabilities2.elementAt(j), capabil[0], capabil[1], capabil[2], capabil[3], capabil[4]);
955 }
956 sigDataSource.addSigner((X509Certificate[]) certChainArray.elementAt(i), (PrivateKey) privKeyArray.elementAt(i), incl2[0], incl2[1], (String) digestArray2.elementAt(i));
957 }
958 for (int i = 0; i < aditionalCerts.size(); i++) {
959 sigDataSource.addCertificate((X509Certificate) aditionalCerts.elementAt(i));
960 }
961 message.setDataHandler(new DataHandler(sigDataSource));
962 message.setDescription("Enveloped and Signed SMIME message.");
963 }
964
965 message.setDisposition(message.ATTACHMENT);
966 SimpleTimeZone tz = (SimpleTimeZone) SimpleTimeZone.getDefault();
967 GregorianCalendar cal = new GregorianCalendar(tz);
968
969 message.setSentDate(cal.getTime());
970
971 clean();
972 } catch (Exception e) {
973 throw SMIMEException.getInstance(this, e, "signingAndEnveloping");
974 }
975 }
976
977 /***
978 * Returns SMIME Message.
979 * @return Signed and encrypted (or encrypted and signed) S/MIME message.
980 */
981 public MimeMessage getSignedAndEnvelopedSMimeMessage() {
982 return message;
983 }
984
985 /***
986 * Sends S/MIME message to SMTP host
987 * @exception MessagingException caused by use of methods from objects of class
988 * Transport.
989 */
990 public void send() throws MessagingException {
991 Transport.send(message);
992 }
993
994 /***
995 * Releases unnecessary memory
996 */
997 private void clean() {
998 ksArray = null;
999 digestArray = null;
1000 including = null;
1001 certChainArray = null;
1002 privKeyArray = null;
1003 digestArray2 = null;
1004 including2 = null;
1005 bodyPartArray = null;
1006 aditionalCerts = null;
1007 certArray = null;
1008 capabilitiesTemp = null;
1009 capabilities = null;
1010 capabilities2 = null;
1011 System.gc(); // Calling garbage collector
1012 }
1013 }
1014
This page was automatically generated by Maven